/****************************************************************************

	COPYRIGHT(C) MAXSI SOFTWARE, JONAS 'SORTIE' TERMANSEN 2008, 2009, 2010

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:
		* Redistributions of source code must retain the above copyright
		  notice, this list of conditions and the following disclaimer.
		* Maxsi Distribution may not be used for evil. If you intend to use this
		  software for evil, you must purchase a license to use Maxsi Distribution
		  for evil from Maxsi Software.

	THIS SOFTWARE IS PROVIDED BY JONAS 'SORTIE' TERMANSEN ''AS IS'' AND ANY
	EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
	DISCLAIMED. IN NO EVENT SHALL JONAS 'SORTIE' TERMANSEN BE LIABLE FOR ANY
	DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
	ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	IN ADDITION, IN NO EVENT SHALL JONAS 'SORTIE' TERMANSEN BE LIABLE IF ANY USERS
	OF THIS SOFTWARE BECOME INFECTED WITH SWINE FLU OR ANY OTHER INFECTIOUS
	DECEASES (INCLUDING, BUT NOT LIMITED TO, BIRD FLU AND ZOMBIE OUTBREAKS), EVEN
	IF ADVISED OF THIS POSSIBILITY. (YES, THIS HAPPENED DURING INTERNAL BETA-TESTING)

	Maxsi Distribution SDK
	An interface to the Maxsi Distribution Platform.

	MaxsiDistributionSDK.cpp
	Code that links to MaxsiDistribution.dll and loads the g_MaxsiDistribution
	interface that can be used by the end product.

****************************************************************************/

#include "cbase.h"

#define MAXSIDISTRIBUTIONSDK_CPP
#include "MaxsiDistributionSDK.h"

/****************************************************************************
	PLEASE CUSTOMIZE THE FOLLOWING
****************************************************************************/

// Enter the name of the folder this mod is within, for instance,
// c:/program files/valve/steam/sourcemods/Altered Transmission/
// should be "Altered Transmission"
#define	MOD_FOLDER_NAME		"Altered Transmission" // Todo: Is this obsolete?

// Enter the name of this mod as registered on the Maxsi Distribution
// master content server in charge this mod.
#define	MOD_NAME			"Altered Transmission - Demo 10"

// Maxsi Distribution can automatically detect all content servers by contacting
// a master server that keeps track of everything. Simply register your content
// servers and your product at a working Maxsi Distribution master server and
// every detail about your mod should be automatically downloaded. Enter the url
// (excluding protocol scheme) to the master server you are using
#define MOD_MASTER_SERVER	"sortie.maxsi.dk/distribution/"

// If you would like to present the end users with a feedback form after
// they quit this mod, which allows them to very easily send their thoughts
// on your product back to you, then set this to 1. If not, then set
// this to 0.
#define	MOD_FEEDBACK		0

// If you would like to limit the people allowed to run your mod to a closed
// number of Steam User accounts, set this to 1. This can also be done via
// server-side code on the Content Servers, so this isn't recommended.
// However, if you really want this DRM security layer, feel free to use it
// and enter the exact Steam Usernames of your users below.
// PLEASE NOTE THAT MAXSI DISTRIBUTION IS NOT DRM AND IS NOT DESIGNED TO BE
// RELIABLE OR FOOL-PROOF AND THAT I, JONAS 'SORTIE' TERMANSEN DISCLAIMS ALL
// WARRENTIES WHETHER ANY DRM-LIKE SYSTEMS DESIGNED BY ME WORK OR NOT.
#define MOD_USERNAME_DRM	0

// If you don't want to link to MaxsiDistribution.dll for some reason, set this
// to 1. Otherwise leave it at 0. Please note that g_MaxsiDistribution will be
// NULL if you enable this setting. This option is not supported and may be
// removed in the future.
#define MOD_DONT_LINK		0

// If you do not wish to have automatic updates, simply disable this.
#define MOD_AUTOMATIC_UPDATING 0

// Creates the optional Maxsi Distribution Modules provided in the Maxsi Distribution SDK.
// Feel free to outcomment the interfaces your Maxsi Distribution Powered Application
// does not use. For more information on the interfaces provided in the Maxsi Distribution
// SDK please see http://www.maxsi.dk/developer/index.php?title=Interfaces

void MaxsiDistributionClass::CreateInterfaces()
{
#ifndef MD_MP_SERVER // Some Interfaces are >not< available Server-Side for MP Mods.
	// Maxsi Achievements
	g_MaxsiAchievements		=	(MaxsiAchievementsInterface*)d_MaxsiDistribution.
								GetInterface(MAXSIACHIEVEMENTS_INTERFACE_VERSION);
	g_MaxsiDistribution		->	RegisterModule(g_MaxsiAchievements);
#endif

	return;
}

#if MOD_USERNAME_DRM

	// To use this DRM system, find the usernames of everyone who may run this product.
	// Make sure these usernames are in LOWER CASES. For instance 'Sortie' becomes
	// 'sortie'. Then using a SHA1 Generator, calculate the SHA1 hash of every lower-case username
	// and enter them into the array below. For instance, the Steam Username 'sortie' has the SHA1
	// Hash 092EC58085AE5E368BC39D18752740FF81A2D09C which becomes the entry in the array below
	// SHA1_t(0x092EC580, 0x85AE5E36, 0x8BC39D18, 0x752740FF, 0x81A2D09C ), // Sortie

	SHA1_t	AllowedUsernamesHashes[]	=
	{
		SHA1_t(0x092EC580, 0x85AE5E36, 0x8BC39D18, 0x752740FF, 0x81A2D09C), // Sortie
		SHA1_t(0x3674951E, 0xC264A721, 0x68CB2D89, 0xA5F634E5, 0x12F6629D), // enter
		SHA1_t(0x4DC73741, 0xB9473168, 0x444FAB7E, 0x680B439B, 0xA69F41EC), // your
		SHA1_t(0xBF2903B5, 0x5E59523C, 0x3F2722F3, 0xB7538BB9, 0x527249EE), // steam
		SHA1_t(0x10DB2797, 0xDF2C0CE9, 0x2CBBAE41, 0x4E8A6B8A, 0x2352E0EE), // accounts
		SHA1_t(0x0154C0DA, 0xBE7145DB, 0x37E0B81B, 0xF667E722, 0x4ABEC0BA), // here

		// Don't remove or modify the following line.
		SHA1_t(0xDA39A3EE, 0x5E6B4B0D, 0x3255BFEF, 0x95601890, 0xAFD80709), // Empty String.
	};

	char*	AllowedUsernames[]	=
	{
		"Sortie",
		"Enter",
		"Your",
		"Steam",
		"Accounts"
		"Here",

		"\\LastUserName", // Don't remove or modify this line.
	};

	// PLEASE NOTE THAT THIS SYSTEM MAY NOT BE RELIABLE AND THAT IT CAN
	// EASILY BE REMOVED/CHANGED IF YOUR BINARIES ARE NOT SIGNED. MAXSI DISTRIBTUION
	// DOES NOT PROVIDE ANY CRYPOGRAPHIC FUNCTIONS THAT CAN DETECT IF THE
	// USERNAME LIST HAS BEEN CHANGED. DO NOT RELY ON WHETHER THE DETECTED
	// STEAM USERNAME IS CORRECT NOR IF IT'S ON THE LIST ABOVE, UNLESS YOU HAVE
	// REASON TO BELIEVE THAT THIS LIST HAS NOT BEEN CHANGED AND THAT THE
	// STEAM USERNAME DETECTED IS CORRECT.

#endif

/****************************************************************************
	DON'T EDIT ANYTHING BELOW.
	(Unless you know what you are doing, of course)
****************************************************************************/

#ifndef MD_MP_SERVER // Some ConVars are not used server side in MP mods.
ConVar md_developer	( "md_developer", "0", 0, "Controls what mode Maxsi Distribution is in. 0 = Default, 1 = Developer (More Verbose).");
#endif

// Should help players get the exact support information.
#define	SupportId(Id) "ErrorID: ME" #Id ", see www.maxsi.dk/support/ for more information."

// A global variable to the Interface. Call MaxsiDistribution::InitializeMaxsiDistribution() to initialize it.
MaxsiDistributionInterface*					g_MaxsiDistribution			=	NULL;
MaxsiAchievementsInterface*					g_MaxsiAchievements			=	NULL;
MaxsiDistributionClass						d_MaxsiDistribution;

	char* MDSeekpaths[] =
	{
		"HKEY_CURRENT_USER\\SOFTWARE\\MaxsiDistribution", // Loads from the registry.
		"../../Maxsi Distribution/", // = %steam%/SourceMods/Maxsi Distribution/
		"", // = The bin folder of the current mod. (Not recommended)
	};
		
	#define	SEEK_PATHS							3

	char* Builds[] =
	{
		"MaxsiDistribution.ini", // Decides what build to use by loading the MaxsiDistribution.ini file
		// looking up the [Global] section to file the PreferredBuildxx (xx=32 for 32-bit md, etc).
		// Please do not remove MaxsiDistribution.ini from here. People should be able to override any
		// settings regarding what distribution they want to use. However, if you prefer that people
		// use your custom distribution of Maxsi Distribution, do add it beneath, and increment
		// BUILDS by one.
		
		//"YourBuildHere/",		// Loads a custom build, if present.
		"Sortie/",				// Loads the official Sortie build.
		"",						// Loads whatever is in the search path folder. (Not recommended)
	};

	#define	BUILDS								3

	char* BinFolders[] =
	{
		"dev" PSIZESTR "/",	// Loads whatever is in the devxx/ folder, if present.
		"bin" PSIZESTR "/", // Loads the official release of the current build of Maxsi Distribution.
		"",		// Loads from the search path folder.
	};

	#define	BINARY_FOLDERS						3

	char* Binaries[] =
	{
		"MaxsiDistribution.dll",
		"MaxsiDistributionUpdate.dll",
		"MaxsiFeedback.exe",
		"MaxsiInstaller.exe",
	};

	// Defined in MaxsiDistribution.h
	//#define	BINARY_MAXSIDISTRIBUTION_DLL			0
	//#define	BINARY_MAXSIDISTRIBUTIONUPDATE_DLL		1
	//#define	BINARY_MAXSIFEEDBACK_EXE				2
	//#define	BINARY_MAXSIINSTALLER_EXE				3

/****************************************************************************
	Common functions from MaxsiEngine.dll
****************************************************************************/

	char* MaxsiDistributionClass::BuildString(unsigned int NumParameters, ...)
	{
		va_list param_pt;

		va_start(param_pt,NumParameters); // Call the setup macro
		
		// First calculate the string length

		size_t FinalString_Len = 0;
		char* TMPString = 0;

		for (unsigned int I = 0 ; I < NumParameters ; I++)
		{
			TMPString = va_arg(param_pt,char*);
			if ( TMPString )
			{
				FinalString_Len+=strlen(TMPString);
			}
		}
		FinalString_Len++;


		// Allocate the required string
		char* FinalString = new char[FinalString_Len+10];
		FinalString[0] = 0;

		va_end(param_pt); // Closing macro
		va_start(param_pt,NumParameters); // Call the setup macro

		for (unsigned int I = 0 ; I < NumParameters ; I++)
		{
			TMPString = va_arg(param_pt,char*);
			if ( TMPString )
			{
				strcat_s(FinalString,FinalString_Len,TMPString);
			}
		}
		
		return FinalString;
	}

	bool MaxsiDistributionClass::str_replace(char* input, char* find, char* replace)
	{
		if (!input||!find||!replace) { return false; }
		size_t input_Len = strlen(input);
		size_t find_Len = strlen(find);
		size_t replace_Len = strlen(replace);

		if (find_Len != replace_Len)
		{
			return false;
		}

		size_t X = 0, Y = 0;

		// Loop through X and check at each X if Y follows, if so return X
		for (X = 0; X < input_Len; X++)
		{
			for (Y = 0; Y < find_Len; Y++)
			{
				if ( input[X+Y] != find[Y] )
				{
					break; // Nope.
				}
			}
			if ( Y == find_Len )
			{
				memcpy(input+X,replace,find_Len);
			}
		}
		return true; // Done
	}

	char*	MaxsiDistributionClass::GetWorkingDir()
	{
		char*	CurrentDirectory		=	0;
		char*	CurrentDirectory_TMP	=	_getcwd(NULL,0);

		size_t CurrentDirectory_TMP_Len = strlen(CurrentDirectory_TMP);

		if (CurrentDirectory_TMP[CurrentDirectory_TMP_Len-1] == '\\' ||
			CurrentDirectory_TMP[CurrentDirectory_TMP_Len-1] == '/')
		{
			CurrentDirectory = new char[CurrentDirectory_TMP_Len+1];
			strcpy_s(CurrentDirectory,CurrentDirectory_TMP_Len+1,CurrentDirectory_TMP);
		}
		else
		{
			CurrentDirectory = new char[CurrentDirectory_TMP_Len+2];
			strcpy_s(CurrentDirectory,CurrentDirectory_TMP_Len+1,CurrentDirectory_TMP);
			memset(CurrentDirectory+CurrentDirectory_TMP_Len,'/',1);
			memset(CurrentDirectory+CurrentDirectory_TMP_Len+1,0,1);
		}

		free(CurrentDirectory_TMP);

		return CurrentDirectory;
	}

/****************************************************************************
	Purpose: An easy method to report any errors to the users.
****************************************************************************/
	bool	MaxsiDistributionClass::ReportError(const char* Error, bool DontShowNotepad)
	{
		IngameError(Error);
		int		FileHandle	=	0;			
		char*	WorkingDir	=	GetWorkingDir();
		char*	File		=	BuildString(2,WorkingDir,"MaxsiDistributionError.txt");

		_sopen_s(&FileHandle,File, _O_CREAT | O_TRUNC | O_BINARY | O_WRONLY, _SH_DENYNO, _S_IREAD | _S_IWRITE);

		if (FileHandle == -1)
		{
			delete[] WorkingDir;
			delete[] File;
			return false;
		}
		else
		{
			char* Footer =
				"\r\n\r\n"
				"This mod is using Maxsi Distribution Features.\r\n"
				"Unfortunately we couldn't initialize Maxsi Distribution.\r\n"
				"Please make sure you have installed Maxsi Distribution properly.\r\n"
				"For more information please see http://www.maxsi.dk/distribution/\r\n"
				"\r\n"
				"Please contact the developers of this mod for more information.\r\n"
				"Please include the error message above.";

			_write(FileHandle,Error,strlen(Error));
			_write(FileHandle,Footer,strlen(Footer));
			_close(FileHandle);		
		}
		
		char*	ReadMeCommand	=	BuildString(3,"notepad \"", File, "\"");

		if ( !DontShowNotepad )
		{
			WinExec(ReadMeCommand,SW_SHOW);
		}
			
		delete[] ReadMeCommand;
		delete[] WorkingDir;
		delete[] File;
		
		return true;
	}

	// Quick console error reporting
	bool MaxsiDistributionClass::IngameError(const char* Error)
	{
		Color ConsoleColor(255,127,63,255);
		ConColorMsg(ConsoleColor,"Maxsi Distribution Error: ");
		ConColorMsg(ConsoleColor,Error);
		ConColorMsg(ConsoleColor,"\n");
		return true;
	}

	// Quick console information
	bool MaxsiDistributionClass::IngameInfo(const char* Error)
	{
		Color ConsoleColor(255,127,63,255);
		ConColorMsg(ConsoleColor,"Maxsi Distribution Info: ");
		ConColorMsg(ConsoleColor,Error);
		ConColorMsg(ConsoleColor,"\n");
		return true;
	}

	//=============================================================================
	//	bool	IngameMsg(char* Msg);
	//
	//	Writes Msg to the console.
	//=============================================================================

	bool	MaxsiDistributionClass::IngameMsg(const char* Msg)
	{
		Color ConsoleColor(255,127,63,255);
		ConColorMsg(ConsoleColor,Msg);
		return true;
	}

	//=============================================================================
	//	bool	IngameMsgDebug(char* Msg);
	//
	//	Writes Msg to the console.
	//=============================================================================

	bool	MaxsiDistributionClass::IngameMsgDebug(const char* Msg)
	{
#ifndef MD_MP_SERVER
		if ( md_developer.GetBool() == false ) { return false; }
#endif
		Color ConsoleColor(255,127,63,255);
		ConColorMsg(ConsoleColor,Msg);
		return true;
	}


	char*	MaxsiDistributionClass::GetSteamUsername()
	{
		char*	WorkingDir		=	GetWorkingDir();
		str_replace(WorkingDir,"\\","/");
		size_t	WorkingDirLen	=	strlen(WorkingDir);
		size_t	UsernameBegin	=	0;
		size_t	UsernameEnd		=	0;
		size_t	UsernameLen		=	0;
		char*	Username		=	0;
		int		NumSlashes		=	0;

		for (size_t N = WorkingDirLen; N > 0; N--)
		{
			if (WorkingDir[N] == '/'||
				WorkingDir[N] == '\\')
			{
				NumSlashes++;
				if ( NumSlashes == 1)
				{
				}
				else if ( NumSlashes == 2)
				{
					UsernameEnd				=	N-1;
				}
				else if ( NumSlashes == 3)
				{
					// Gather the username
					UsernameBegin			=	N+1;
					UsernameLen				=	UsernameEnd-UsernameBegin+1;
					Username				=	new char[UsernameLen+1];

					memcpy(Username,WorkingDir+UsernameBegin,UsernameLen);

					Username[UsernameLen]	=	0;
				}
			}
		}
		return Username;
	}

	char* MaxsiDistributionClass::GetRegMaxsiRoot( void )
	{
		char*	MDRoot = NULL;
		char	lszValue[MAX_PATH];

		HKEY	hKey;
		LONG	returnStatus;

		size_t	dwType	=	REG_SZ;
		size_t	dwSize	=	MAX_PATH;

		returnStatus = RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\MaxsiDistribution\\", 0L,  KEY_READ, &hKey);

		if (returnStatus == ERROR_SUCCESS)
		{
			returnStatus = RegQueryValueExA(hKey, "Root", NULL, (LPDWORD)&dwType,(LPBYTE)&lszValue, (LPDWORD)&dwSize);

			if (returnStatus == ERROR_SUCCESS)
			{
				MDRoot = new char[dwSize+2];
				MDRoot[dwSize] = 0; // Lets null-terminate!
				memcpy(MDRoot,lszValue,dwSize);
				if (MDRoot[dwSize-2] != '/' &&
					MDRoot[dwSize-2] != '\\' )
				{
					MDRoot[dwSize-1]		= '\\';
					MDRoot[dwSize+0]		= 0;
				}
			}

			RegCloseKey(hKey);	
		}

		return MDRoot;
	}

	// 32 bit signed integer, base 10
	char*	MaxsiDistributionClass::IToA(int	In)
	{
		char*	Result	=	new char[12]; // ceiling(32*ln(2)/ln(10)) + room for a leading '-' + null
		_itoa_s(In,Result,12,10);
		return	Result;
	}

	// TODO: There is no function called _uitoa_s!!! Simply pass it on to the 64-bit version
	// 32 bit unsigned integer, base 10
	char*	MaxsiDistributionClass::UIToA(unsigned int	In)
	{
		return UIToA64((unsigned __int64)In);
		//char*	Result	=	new char[11]; // ceiling(32*ln(2)/ln(10)) + null
		//_uitoa_s(In,Result,11,10);
		//return	Result;
	}
	
	// 64 bit signed integer, base 10
	char*	MaxsiDistributionClass::IToA64(__int64	In)
	{
		char*	Result	=	new char[22]; // ceiling(64*ln(2)/ln(10)) + room for a leading '-' + null
		_i64toa_s(In,Result,22,10);
		return	Result;
	}

	// 64 bit unsigned integer, base 10
	char*	MaxsiDistributionClass::UIToA64(unsigned __int64	In)
	{
		char*	Result	=	new char[21]; // ceiling(64*ln(2)/ln(10)) + null
		_ui64toa_s(In,Result,21,10);
		return	Result;
	}

	//=============================================================================
	//	SHA1_t	CalculateSHA1(char* Buffer, size_t BufferLen);
	//
	//	Calculates a 160-bit SHA1 hash
	//=============================================================================

	SHA1_t	MaxsiDistributionClass::CalculateSHA1(char* Buffer, size_t BufferLen)
	{
		// TODO: >NOT< implemented yet!
		return SHA1_t(0x01234567,0x89ABCDEF,0xFEDCBA98,0x76543210,0x01234567);
	}

	//=============================================================================
	//	MaxsiDistributionClass::d_MaxsiDistribution()
	//	MaxsiDistributionClass::~d_MaxsiDistribution()
	//
	//	Makes sure the MaxsiDistribution.dll is unloaded when the game quits.
	//	Oh! And variables are initialized here too!
	//=============================================================================

	MaxsiDistributionClass::MaxsiDistributionClass()
	{
		MaxsiDistributionLibrary	=	NULL;
		iSeekPath					=	0;
		iBuild				=	0;
		iBinFolder					=	0;
		iBinary						=	BINARY_MAXSIDISTRIBUTION_DLL;
	}

	MaxsiDistributionClass::~MaxsiDistributionClass()
	{			
		if ( MaxsiDistributionLibrary ) 
		{
			FreeLibrary(MaxsiDistributionLibrary);
			MaxsiDistributionLibrary	=	NULL;
		}

#ifdef CLIENT_DLL
#if MOD_FEEDBACK

		LPSTARTUPINFOA si			=	new _STARTUPINFOA;
		LPPROCESS_INFORMATION pi	=	new _PROCESS_INFORMATION;

		ZeroMemory( si, sizeof(_STARTUPINFOA) );
		si->cb = sizeof(si);
		ZeroMemory( pi, sizeof(_PROCESS_INFORMATION) );

#ifdef GAME_DLL
		char* GameDir = new char[1024];
		engine->GetGameDir((char*)GameDir, 1024);
#else
		char* GameDir = (char*)engine->GetGameDirectory();
#endif

		bool	Success		=	false;

		for ( size_t I = 0; I < BINARY_FOLDERS; I++ )
		{
			char* TheCommand = BuildString(8,"\"",GameDir,"/bin/",MDSeekpaths[iSeekPath],BinFolders[iBinFolder],Binaries[BINARY_MAXSIFEEDBACK_EXE],"\" \"",GameDir,"\"");

			if( CreateProcessA( NULL,   // No module name (use command line)
				TheCommand,        // Command line
				NULL,           // Process handle not inheritable
				NULL,           // Thread handle not inheritable
				FALSE,          // Set handle inheritance to FALSE
				0,              // No creation flags
				NULL,           // Use parent's environment block
				NULL,           // Use parent's starting directory 
				si,            // Pointer to STARTUPINFO structure
				pi )           // Pointer to PROCESS_INFORMATION structure
			)
			{ 
				Success		=	true;

				CloseHandle( pi->hProcess );
				CloseHandle( pi->hThread );
				delete[] TheCommand;
				break;
			}

			delete[] TheCommand;
		}

		if (!Success)
		{
			char* Error = BuildString(1,"LaunchFeedbackApplication::~LaunchFeedbackApplication(), CreateProcessA() Failed! " SupportId(37));
			ReportError(Error);
			delete[] Error;
		}

#ifdef GAME_DLL
		delete[] GameDir;
#endif
		delete pi;
		delete si;
#endif
#endif

	}

	//=============================================================================
	//	bool	InitializeMaxsiDistribution();
	//
	//	Links to MaxsiDistribution.dll and sets g_MaxsiDistribution. If an error,
	//	occurs the function returns false and the game quits and an error message
	//	is presented. If true, the game can safely continue.
	//=============================================================================

	bool	MaxsiDistributionClass::InitializeMaxsiDistribution()
	{
		// Maxsi Distribution.

		int		iBinary		=	BINARY_MAXSIDISTRIBUTION_DLL;

		// Whether we need to try again.
		bool	Relink		=	true;
		int		Result		=	RESULT_DONT_RUN;

		while	(Relink)
		{
			if ( d_MaxsiDistribution.InitializeMaxsiDistributionLibrary(iBinary) )
			{
#if MOD_DONT_LINK
				return true;
#endif
#if defined(CLIENT_DLL) && MOD_AUTOMATIC_UPDATING
				Result	=	g_MaxsiDistribution->UpdateProduct();
#else
				Result	=	RESULT_RUN;
#endif

				if ( Result == RESULT_DONT_RUN )
				{
					Relink	=	false;
				}
				else if ( Result == RESULT_RUN )
				{
					Relink	=	false;
				}
				else if ( Result == RESULT_UPDATE_UPDATING_SOFTWARE )
				{
					iBinary	=	BINARY_MAXSIDISTRIBUTIONUPDATE_DLL;
					Relink	=	true;
				}
				else if ( Result == RESULT_UPDATE_UPDATED_SOFTWARE )
				{
					iBinary	=	BINARY_MAXSIDISTRIBUTION_DLL;
					Relink	=	true;
				}
			}
			else
			{
				Relink		=	false;
			}
		}

		if ( Result == RESULT_RUN )
		{
#ifdef GAME_DLL
			g_pGameSaveRestoreBlockSet->AddBlockHandler( GetMaxsiDistributionSaveRestoreBlockHandler() );
			// TODO: Call g_pGameSaveRestoreBlockSet->RemoveBlockHandler just
			// before CServerGameDLL::DLLShutdown()!
#endif
			
			// Create the g_Maxsi* Interfaces needed by this application.
			CreateInterfaces();

			g_MaxsiDistribution->ReportCrashes();
			//g_MaxsiDistribution->Statistic("Progress","Launched");

			return true;
		}
		else
		{
			//PostQuitMessage(0);

			// Todo: Is this dangerous?
			ExitProcess(0);

			return false;
		}
	}

	//=============================================================================
	//	bool	InitializeMaxsiDistributionLibrary(int Binary);
	//
	//	Links to MaxsiDistribution.dll and sets g_MaxsiDistribution. If an error,
	//	occurs the function returns false and the game quits and an error message
	//	is presented. If true, the game can safely continue.
	//=============================================================================

	bool	MaxsiDistributionClass::InitializeMaxsiDistributionLibrary(int Binary)
	{
		if ( MaxsiDistributionLibrary ) 
		{
			FreeLibrary(MaxsiDistributionLibrary);
			MaxsiDistributionLibrary	=	NULL;
		}

		// Reinitialize variables - in case we are not called for the first time!

		MaxsiDistributionLibrary	=	NULL;
		iSeekPath					=	0;
		iBuild						=	0;
		iBinFolder					=	0;
		iBinary						=	Binary;

		bool	Result			=	false;
		char*	GameDir			=	0;

		char*	Username		=	GetSteamUsername();

#if MOD_USERNAME_DRM

		size_t	UsernameCounter	=	0;
		bool	Found			=	false;

		while ( _stricmp(AllowedUsernames[UsernameCounter],"\\LastUserName") != 0 )
		{
			if ( _stricmp(AllowedUsernames[UsernameCounter],Username) == 0 )
			{
				Found			=	true;
				break;
			}
			UsernameCounter++;
		}

		if ( !Found )
		{
			ReportError(
				"You are not allowed to run this product. "
				"Your Username was not on the list of allowed Usernames. "
				"If you do believe you should be allowed to run this mod, "
				"please contact the developers of this mod. "
				"Please don't contact Maxsi Distribution, "
				"we are not responsible for this."
				);

			delete[]	Username;
			return false; // Do not launch this mod.
		}

#endif
		
#if MOD_DONT_LINK

		delete[]	Username;

		return	true;

#endif

		if (!engine)
		{
			ReportError("Maxsi Distribution Error: The in-game variable 'engine' isn't set! " SupportId(2));
			return false;
		}

#ifdef GAME_DLL
		GameDir = new char[1024];
		engine->GetGameDir((char*)GameDir, 1024);
#else
		GameDir = (char*)engine->GetGameDirectory();

		if (!GameDir)
		{		
			ReportError("Maxsi Distribution Error: The in-game variable 'engine->GetGameDirectory()'  isn't set! " SupportId(1));
			return false;
		}
#endif

		// Initialize variables.

		char*	BinFolder		=	BuildString(2,GameDir,"/bin/");
		char*	ProductRoot		=	BuildString(2,GameDir,"/../");
		char*	WorkingDir		=	GetWorkingDir();
		char*	Path			=	0;
		char*	MD_INI			=	NULL;
		char*	MD_SEEK			=	NULL;
		bool	FullSearchPath	=	false;

		// Go through all the search paths in order and find the best binary.
		for ( iSeekPath = 0; iSeekPath < SEEK_PATHS; iSeekPath++ )
		{
			// Load from where Maxsi Distribution is installed according to the registry.
			if ( _stricmp(MDSeekpaths[iSeekPath], "HKEY_CURRENT_USER\\SOFTWARE\\MaxsiDistribution") == 0 )
			{
				char*	PreferredRoot		=	GetRegMaxsiRoot();
				if ( PreferredRoot )
				{
					MD_SEEK					=	MDSeekpaths[iSeekPath];
					MDSeekpaths[iSeekPath]	=	PreferredRoot;
					FullSearchPath			=	true;
				}
				else
				{
					continue;
				}
			}

			for ( iBuild = 0; iBuild < BUILDS; iBuild++ )
			{
				// Load the preferred build from MaxsiDistribution.ini
				if ( _stricmp(Builds[iBuild],"MaxsiDistribution.ini") == 0 )
				{
					char*	Dest		=	new char[MAX_PATH+1];
					char*	MD_INI_FILE =	(FullSearchPath)
							? BuildString(2,MDSeekpaths[iSeekPath],"MaxsiDistribution.ini")
							: BuildString(3,BinFolder,MDSeekpaths[iSeekPath],"MaxsiDistribution.ini");

					str_replace(MD_INI_FILE,"/","\\"); // Silly Windows uses back-slashes

					DWORD	Result	=	GetPrivateProfileStringA(
						"Global",
						"PreferredBuild" PSIZESTR,
						NULL,
						Dest,
						MAX_PATH+1,
						MD_INI_FILE);

					delete[] MD_INI_FILE;
					
					if ( Result == 0 ) // Key/Section/File not found!
					{
						continue;
					}
					else // Found!
					{
						strcat_s(Dest,MAX_PATH+1,"\\");
						MD_INI = Builds[iBuild];
						Builds[iBuild]	=	Dest;
					}
				}

				for ( iBinFolder = 0; iBinFolder < BINARY_FOLDERS; iBinFolder++ )
				{
					char*	Path				=	(FullSearchPath)
							? BuildString(4,MDSeekpaths[iSeekPath],Builds[iBuild],BinFolders[iBinFolder],Binaries[iBinary])
							: BuildString(5,BinFolder,MDSeekpaths[iSeekPath],Builds[iBuild],BinFolders[iBinFolder],Binaries[iBinary]);
					str_replace(Path,"/","\\");

					char*	NewWorkingDir		=	(FullSearchPath)
							? BuildString(3,MDSeekpaths[iSeekPath],Builds[iBuild],BinFolders[iBinFolder])
							: BuildString(4,BinFolder,MDSeekpaths[iSeekPath],Builds[iBuild],BinFolders[iBinFolder]);
					str_replace(NewWorkingDir,"/","\\");
					
					// Change the working directory
					if ( _chdir(NewWorkingDir) != 0)
					{
						// We could not change the working dir, possibly because it does not exist.
						delete[]	NewWorkingDir;
						continue;
					}

					delete[]		NewWorkingDir;

					MaxsiDistributionLibrary	=	LoadLibraryA(Path);
					
					if ( MaxsiDistributionLibrary )
					{
						break;
					}
					else
					{
						delete[] Path;	Path = NULL;
					}
				}

				if ( MD_INI ) // INI file was loaded, clean up memory!
				{
					delete[] Builds[iBuild];
					Builds[iBuild]		=	MD_INI;
					MD_INI				=	NULL;
				}

				if ( MaxsiDistributionLibrary )	{ break; }
			}

			if ( MD_SEEK )
			{
				delete[] MDSeekpaths[iSeekPath];
				MDSeekpaths[iSeekPath]	=	MD_SEEK;
				MD_SEEK					=	NULL;
				if ( !MaxsiDistributionLibrary )
				{
					FullSearchPath		=	false;
				}
			}

			if ( MaxsiDistributionLibrary )	{ break; }
		}		

		if ( MaxsiDistributionLibrary )
		{
			typedef void* (__cdecl* type_GetInterface  ) (char* Version);
			GetInterface = reinterpret_cast<type_GetInterface>(GetProcAddress(MaxsiDistributionLibrary, "GetInterface" ));

			if ( GetInterface )
			{
				g_MaxsiDistribution		=	(MaxsiDistributionInterface*)GetInterface(MAXSIDISTRIBUTION_INTERFACE_VERSION);

				if ( g_MaxsiDistribution == NULL )
				{
					char*		Error		=		BuildString(3,"Maxsi Distribution Error:\r\n\r\nCouldn't Create Interface Version " MAXSIDISTRIBUTION_INTERFACE_VERSION " in ",Path, SupportId(5));
					ReportError(Error);
					delete[]	Error;
				}
				else
				{
					Result	=	true;

					// Initialize Maxsi Distribution, if it haven't already been done.
					if ( g_MaxsiDistribution->IsInitialized() == false)
					{					
						char*	MDRoot			=	(FullSearchPath)
								? BuildString(1,MDSeekpaths[iSeekPath])
								: BuildString(2,BinFolder,MDSeekpaths[iSeekPath]);

						char*	ProductPID		=	UIToA((unsigned int)GetCurrentProcessId());

						// Provide Maxsi Distribution with as much information as possible - in the
						// case that it might need it in the future - right now some of it isn't needed
						// in the stock Maxsi Distribution build. This makes us forward compatible!

						char*	Variables[]		=
						{
							"ContentType",			"Steam/SourceMod", // 1
							"MDPath",				Path, // 2
							"MDRoot",				MDRoot, // 3
							"MDSeekPath",			MDSeekpaths[iSeekPath], // 4
							"MDDistribution",		Builds[iBuild], // 5 // Obsolete: Please delete.
							"MDBuild",				Builds[iBuild], // 6
							"MDBinFolder",			BinFolders[iBinFolder], // 7
							"MDBinary",				Binaries[iBinary], // 8
							"ProductBin",			BinFolder, // 9
							"ProductDirectory",		GameDir, // 10
							"ProductMasterServer",	MOD_MASTER_SERVER, // 11
							"ProductName",			MOD_NAME, // 12
							"ProductPID",			ProductPID, // 13
							"ProductRestartCmd",	GetCommandLineA(), // 14
							"ProductRoot",			ProductRoot, // 15
							"Username",				Username, // 16
							"WorkingDir",			WorkingDir, // 17

							"SDKCallbackVersion",	MAXSIDISTRIBUTION_CLASS_VERSION, // 18
							"SDKCallback",			(char*)&d_MaxsiDistribution, // 19
						};

						g_MaxsiDistribution->Initialize(Variables,19);
					
						delete[]	MDRoot;
					}
				}
			}
			else
			{			
				ReportError("Maxsi Distribution Error:\r\n\r\nCouldn't find function GetInterface in MaxsiDistribution.dll. " SupportId(6));
			}

			// Restore the working directory
			_chdir(WorkingDir);
		}
		else
		{
			LPTSTR pszMessage;
			DWORD dwLastError = GetLastError(); 
			
			FormatMessageA(
				FORMAT_MESSAGE_ALLOCATE_BUFFER | 
				FORMAT_MESSAGE_FROM_SYSTEM |
				FORMAT_MESSAGE_IGNORE_INSERTS,
				NULL,
				dwLastError,
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
				(LPTSTR)&pszMessage,
				0, NULL );

			// Display the error message and exit the process
			char*	Error = BuildString(2,
				MOD_NAME " can't be run without Maxsi Distribution installed. "
				"Maxsi Distribution was not detected on your system. Please install "
				"Maxsi Distribution from http://www.maxsi.dk/distribution/\r\n\r\n"
				"Maxsi Distribution is an open-source next-generation mod distribution platform "
				"offering advanced features such as automatic updating, achievements, "
				"statistics, crash reporting, and much more.\r\n\r\nError: ", pszMessage);

			ReportError(Error);

			LocalFree(pszMessage);
		}

		if ( Path) { delete[] Path; }

		delete[] ProductRoot;
		delete[] BinFolder;
		//delete[] LibraryDLL;
		delete[] WorkingDir;
		delete[] Username;
#ifdef GAME_DLL
		delete[] GameDir;
#endif

		return Result;
	}

// To save Maxsi Distribution Session Data in Save Games!
#ifdef GAME_DLL

	// Increment this if the Maxsi Distribution Block Data Format changes.
	// The current format is:
	// size_t DataLength;
	// char Data[DataLength];

	static short MAXSIDISTRIBUTION_SAVE_RESTORE_VERSION = 1;

	//-----------------------------------------------------------------------------

	class CMaxsiDistributionSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler
	{
	public:
		const char *GetBlockName()
		{
			return "MaxsiDistribution";
		}

		//---------------------------------

		void Save( ISave *pSave )
		{
			pSave->StartBlock( "MaxsiDistribution" );

			char*	Data;
			size_t	DataLength;

			g_MaxsiDistribution->SaveSession(&Data, &DataLength);
			
			pSave->WriteData( (const char*)&DataLength, sizeof(DataLength) );
			pSave->WriteData( (const char*)Data, (int)DataLength );

			g_MaxsiDistribution->SavedSession(Data, DataLength);

			pSave->EndBlock();
		}

		//---------------------------------

		void WriteSaveHeaders( ISave *pSave )
		{
			pSave->WriteShort( &MAXSIDISTRIBUTION_SAVE_RESTORE_VERSION );
		}
		
		//---------------------------------

		void ReadRestoreHeaders( IRestore *pRestore )
		{
			// No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so.
			short version;
			pRestore->ReadShort( &version );
			// only load if version matches and if we are loading a game, not a transition
			m_fDoLoad = ( ( version == MAXSIDISTRIBUTION_SAVE_RESTORE_VERSION ) && 
				( ( MapLoad_LoadGame == gpGlobals->eLoadType ) || ( MapLoad_NewGame == gpGlobals->eLoadType )  ) 
			);
		}

		//---------------------------------

		void Restore( IRestore *pRestore, bool createPlayers )
		{
			if ( m_fDoLoad )
			{
				pRestore->StartBlock();

				size_t	DataLength;

				pRestore->ReadData((char*)&DataLength,sizeof(size_t),0);

				char*	Data	=	new char[DataLength];

				pRestore->ReadData((char*)Data,(int)DataLength,DataLength);

				g_MaxsiDistribution->RestoreSession(Data, DataLength);

				delete[] Data;
				
				pRestore->EndBlock();
			}
		}

	private:
		bool m_fDoLoad;
	};

	//-----------------------------------------------------------------------------

	CMaxsiDistributionSaveRestoreBlockHandler g_MaxsiDistributionSaveRestoreBlockHandler;

	//-------------------------------------

	ISaveRestoreBlockHandler *GetMaxsiDistributionSaveRestoreBlockHandler()
	{
		return &g_MaxsiDistributionSaveRestoreBlockHandler;
	}

#endif